/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "common.h"
#include "abort.h"
#include "container.h"
#include "graphicDevice.h"
#include "messageTranslater.h"
#include "text.h"

namespace Graph2D
{
	Text::Text() : instance(NULL), dirty(true), valid(false)
	{
		setType(TYPE_TEXT);

		setColor(0.0f, 0.0f, 0.0f, 0.0f, 0.0f);

		setOwnerDraw(true);

		instance = Implementation::Text::create(getDefaultFontSize());
	}

	Text::Text(const size_t fontSize) : instance(NULL), dirty(true), valid(false)
	{
		setType(TYPE_TEXT);

		setColor(0.0f, 0.0f, 0.0f, 0.0f, 0.0f);

		setOwnerDraw(true);

		instance = Implementation::Text::create(fontSize);
	}

	Text::Text(const std::string& fontName, const size_t fontSize)
	{
		setType(TYPE_TEXT);

		setColor(0.0f, 0.0f, 0.0f, 0.0f, 0.0f);

		setOwnerDraw(true);

		instance = Implementation::Text::create(fontName, fontSize);
	}

	Text::~Text()
	{
		if(instance)
		{
			Implementation::Text::release(instance);
			instance = NULL;
		}
	}

	void Text::clear()
	{
		if(!text.empty())
		{
			text.clear();
			request();
		}
	}

	void Text::setFontName(const std::string& fontName)
	{
		Implementation::Text::setFontName(instance, fontName);
		request();
	}

	void Text::setFontSize(const float fontSize)
	{
		Implementation::Text::setFontSize(instance, static_cast<size_t>(fontSize));
		request();
	}

	const Color& Text::getTextColor() const
	{
		return textColor;
	}

	void Text::setTextColor(const Color& color)
	{
		textColor = color;
	}

	TextAlignmentH Text::getTextAlignmentH() const
	{
		return TEXT_ALIGNMENT_LEFT;
	}

	void Text::setTextAlignmentH(const TextAlignmentH textAlignment)
	{
		((void)textAlignment);
	}

	TextAlignmentV Text::getTextAlignmentV() const
	{
		return TEXT_ALIGNMENT_TOP;
	}

	void Text::setTextAlignmentV(const TextAlignmentV textAlignment)
	{
		((void)textAlignment);
	}

	const std::string& Text::getString() const
	{
		return text;
	}

	void Text::setString(const std::string& text)
	{
		if(this->text != text)
		{
			this->text = text;

			if(this->text.length() > 0)
			{
				MessageTranslater::translate(this->text);
			}
			else
			{
				this->text.clear();
			}
			request();
		}
	}

	void Text::format(const char* format, ...)
	{
		va_list ap;
		va_start(ap, format);
		vformat(format, ap);
		va_end(ap);
	}

	void Text::vformat(const char* format, va_list ap)
	{
		char *p, *np;
		int size = 100;

		if((p = reinterpret_cast<char*>(malloc(size))) == NULL)
			return;

		for(;;)
		{
#if __STDC_WANT_SECURE_LIB__
			const int n = vsnprintf_s(p, size, _TRUNCATE, format, ap);
#else
			const int n = vsnprintf(p, size, format, ap);
#endif

			if(n > -1 && n < size)
			{
				break;
			}
			if(n > -1)
				size = n + 1;
			else
				size *= 2;
			if((np = reinterpret_cast<char*>(realloc(p, size))) == NULL)
			{
				break;
			}

			p = np;
		}

		setString(std::string(p));

		free(p);
	}

	void Text::replace(const char* key, const char* word)
	{
		const size_t length = strlen(key);

		int position = 0;
		while((position = text.find(key, position)) != std::string::npos)
		{
			text.replace(position, length, word);
		}
	}

	void Text::adjustSize(const bool useParentWindowSize)
	{
		const Graph2D::Vector2& newSize = getAdjustedSize(useParentWindowSize);
		setSize(newSize + 0.5);
	}

	Vector2 Text::getAdjustedSize(const bool useParentWindowSize)
	{
		Vector2 clientSize;
		Container* parent;
		if(useParentWindowSize && (parent = getParent()) != NULL)
			clientSize = parent->getClientSize();
		else
			clientSize.set(2048, 2048);
		return Implementation::Text::getSize(instance, text, clientSize);
	}

	void Text::request()
	{
		adjustSize();
		dirty = true;
		valid = false;
	}

	void Text::onSerialize(mana_stream* stream) const
	{
		super::onSerialize(stream);

		/*
		mana_stream_push_string(stream, (char*)text.c_str());
		mana_stream_push_string(stream, (char*)fontName.c_str());
		mana_stream_push_float(stream, fontSize);
		textColor.serialize(stream);
		*/
		mana_stream_mark(stream);
	}

	void Text::onDeserialize(mana_stream* stream)
	{
		super::onDeserialize(stream);

		/*
		[this setString:[NSString stringWithUTF8String:mana_stream_get_string_pointer(stream)]];
		mana_steram_seek(stream, mana_stream_get_string_length(stream));

		[this setFontName:[NSString stringWithUTF8String:mana_stream_get_string_pointer(stream)]];
		mana_steram_seek(stream, mana_stream_get_string_length(stream));

		[this setFontSize:mana_stream_pop_float(stream)];
		textColor.deserialize(stream);
		*/
		mana_stream_check(stream);
	}

	void Text::onUpdate(const UpdateInfomation& updateInfomation)
	{
		if(dirty)
		{
			const Graph2D::Vector2& size = getSize();
			valid = Implementation::Text::set(
				instance,
				size.x,
				size.y,
				text
			);
			dirty = false;
		}

		super::onUpdate(updateInfomation);
	}

	bool Text::onDraw(const DrawRect& drawRect)
	{
#if 0
		if(!super::onDraw(drawRect))
			return false;
#endif
		if(valid)
		{
			Texture::unbind();
			GraphicDevice::enableTexture(true);
			GraphicDevice::enableOpaque(false);
			//GraphicDevice::enableScissor(false);
			GraphicDevice::enableColorArray(false);
			GraphicDevice::enableTextureCoordArray(true);
			{
				const Vector2 drawPosition = GraphicDevice::translateDisplayPosition(drawRect.getDrawLeftTopPosition());
				const Vector2 drawSize = drawRect.getDrawSize() * GraphicDevice::getScreenToDisplayScale();

				const Color currentColor = getDrawColor(drawRect, textColor);

				const Color shadowColor(currentColor.r * 0.1f, currentColor.g * 0.1f, currentColor.b * 0.1f, currentColor.a);
				GraphicDevice::setColor(shadowColor);
				Implementation::Text::draw(instance, drawPosition + Vector2(1.f, 1.f), drawSize);

				GraphicDevice::setColor(currentColor);
				Implementation::Text::draw(instance, drawPosition, drawSize);
			}
			Texture::unbind();
			GraphicDevice::applyDrawArrayPointer();
		}

		return true;
	}

	bool Text::compare(const Text& other) const
	{
		if(!super::compare(other))
			return false;
		if(text != other.text)
			return false;
		if(textColor != other.textColor)
			return false;
		if(Implementation::Text::getFontSize(instance) != Implementation::Text::getFontSize(other.instance))
			return false;
		if(strcmp(Implementation::Text::getFontName(instance), Implementation::Text::getFontName(other.instance)) != 0)
			return false;
		return true;
	}
}
